#coding: utf-8

import Operators
import Register
import AsmPanic
import Instruction

'''
    Motor de execução do ASM
    @todo: então manter um... hã, registro dos registradores sendo escritos em runtime.
    @todo: ser capaz de reordenar o codigo
    @author: iuri.andreazza
'''
class AsmEngine:
    
    '''
        @var __instr: Lista das Instruções que devem ser executadas
    '''
    __instr = [];
    
    '''
        @var __pastInstr: Instruções executadas
    '''
    __pastInstr = [];
    
    '''
        @var __tCicles: Dicionário dos Ciclos de cada Instrução
    '''
    __tCicles = {"lw"  : {"BI": 2, "LR": 1, "OU": 2, "AD": 2, "ER":1}, # lw
                 "sw"  : {"BI": 2, "LR": 1, "OU": 2, "AD": 2, "ER":0}, # sw
                 "fn"  : {"BI": 2, "LR": 1, "OU": 2, "AD": 0, "ER":1}, # add, sub, and, or, slt
                 "beq" : {"BI": 2, "LR": 1, "OU": 2, "AD": 0, "ER":0}  # beq
                 };
    
    '''
        @var __pMatriz: Matriz que simula a execução dos Pipelines
    '''
    __pMatriz = []; #matriz que simula a execução dos Pipelines
    
    '''
        @var __pLines: Numero de Linhas na atual execução do Pipeline
    '''
    __pLines = 0; #numero de Pipelines.
    
    '''
        @var __pStages: Estagios da execução do Pipeline
    '''
    __pStages = ["BI","LR","OU","AD","ER"];
    
    '''
        @var __pp: Program Pointer (Indica em que indice do array de instruções está.)
    '''
    __pp = 0; #Program Pointer (Indica em que indice do array de instruções está.
    
    '''
        @var __memory: Array da Memória
    '''
    __memory = [];
    
    '''
        @var __regs: Array dos Registradores
    '''
    __regs = [];
    
    '''
        @var __regsInUse: Array dos Registradores em Uso
    '''
    __regsInUse = [];
    
    __regsNumMax = 32;
    
    '''
       Construtor 
    '''
    def __init__(self, numLines = 5, numRegs = 32, tamMemory = 256):
        self.__instr = [];
        self.__regsNumMax = numRegs;
        for j in range(numRegs):
            self.__regs.append(0);
            
        for j in range(tamMemory):
            self.__memory.append(0);
            
        for i in range(numLines):
            self.__pMatriz.append(None);
        
    '''
        Carrega as instruções no Engine e reseta o PP
        
        @param instr: Instruções
        @return: void
    '''
    def loadInstructions(self, instr):
        self.__pp = 0;
        self.__instr = instr;
        print str(len(self.__instr))+" linhas de instruções carregadas." #Informa quantas linhas foram carregadas.
        print ""
        
    '''
        Executa as instruções
    '''
    def doExecute(self):
        contLine = 1
        
        while(len(self.__instr) > 0):
            if(contLine < 10): 
                strContLine = "0"+str(contLine)
            else:
                 strContLine = str(contLine)
            print strContLine+"-",
            self.__loadInstrcIntoPipe();
            self.advPipes();  
            contLine+=1          
        while(self.__pLines > 0):
            if(contLine < 10): 
                strContLine = "0"+str(contLine)
            else:
                 strContLine = str(contLine)
            print strContLine+"-",
            self.advPipes();
            contLine+=1       
        return 1;
            
    '''
        Indicador se o Pipe está cheio.
        
        @return: boolean: True (cheio)
    '''
    def pipeIsFull(self):
        return len(self.__pMatriz) == self.__pLines;
    
    '''
        Indicador se o Pipe está vazio.
        
        @return: boolean: True (vazio)
    '''
    def pipeIsEmpty(self):
        return len(self.__pMatriz) == 0;
        
    '''
        Indicador se o Pipe está carregando uma instrução.
        
        @return: boolean: True (Carregando)
    '''
    def __pipeIsLoadingInstrc(self):
        for it in self.__pMatriz:
            if(it != None):
                if(it.getStage() == 0):
                    return True;
        return False;
    
    '''
        Adiciona uma Instrução ao Pipe
        
        @param instrc: Instrução a Ser adicionada
        @return: void
    '''
    def __addPipeInstruction(self, instrc):
        fSlot = 0;
        for it in self.__pMatriz:
            if(self.__pMatriz[fSlot] == None):
                break;
            else:
                fSlot += 1;
                
        self.__pMatriz[fSlot] = instrc;
        self.__pMatriz[fSlot].advPipeStage();
        self.__pLines += 1;
    
    
    '''
        Carrega uma instrução ao Pipe em caso de ele não estar cheio 
        ou não estar carregando uma instrução.
        
        @raise AsmPanic: Erro Generalizado durante a execução
        @return: void
    '''
    def __loadInstrcIntoPipe(self):
        if(self.pipeIsFull() == False and not self.__pipeIsLoadingInstrc()): # Se o Pipe não está cheio e não está carregando alguma instrc.
            self.__addPipeInstruction(self.__instr[self.__pp]); #adiciona a instrução ao Pipe
            regs = self.__instr[self.__pp].getOperandos(); #retorna os Operandos da Instrução
            
            #reconhecer os registradores 
            self.__addRegs(regs);
            self.__pastInstr.append(self.__instr.pop(self.__pp)); #remove a instrução da lista de execução
            self.__pp += 1;
    
    '''
        Adiciona os registradores utilizados na instrução
    '''
    def __addRegs(self, regs):
        for reg in regs:
            if(reg.getNumero() > self.__regsNumMax): #caso o numero do registrador tenha sido colocado alem do maximo de regs q tem o sistema
                raise AsmPanic("Linha: "+self.__instr[self.__pp].getLabel()+" - Registrador Inexistente");
            self.__regsInUse.append(reg.getNumero()); #adiciona o Indice do registrador na lista dos que estao sendo usados
    
    def __rmRegs(self, regs):
        for reg in regs:
            self.__regsInUse.remove(reg.getNumero());
    
    '''
        Reporta o avanço dos estagios do Pipe
        
        @param instrc: Instrução
        @param msg: Mensagem
        @return: void
    '''
    def __reportAdvPipe(self, instrc, msg = " "):
        if(instrc != None):
            print "["+str(instrc.getLabel()) + " - " +str(instrc.getStage())+"]"+msg,;
        else:
            print msg,;
    '''
        Retorna a próxima Instrução a ser executada a partir da instrução indicada
        
        @param it: Instrução
        @return: Instruction: Próxima instrução
    '''
    def __getNextInstrcPipe(self, it):
        idx = self.__pMatriz.index(it); #acha o indice da instrução informada no pipe
        if(idx < self.__pLines-1): # se o indice da instrução no Pipe não é a Ultima Linha 
            if(self.__pMatriz[idx+1] != None): #testa se o seguinte tem uma instrução carregada
                return self.__pMatriz[idx+1]; #retorna a proxima instrução no pipe
            else:
                return it; #retorna o proprio item
        else:            
            return it; #retorna o proprio item
        
    
    '''
        @todo: FAZER nessa função o tratamento de conflitos para os registradores e tal.
    '''
    def advPipes(self):
        stageInUse = []; #array dos estagios em uso
        i = 0;
#        "BI","LR","OU","AD","ER"
        for it in self.__pMatriz:
            #se a linha do pipe não está vazia
            if(it != None): 
                #Se N passou por todos os estagios
                if(it.getStage() < len(self.__pStages) ):
                    #Se o estagio da Instrução Atual NÃO ESTA EM USO por outra instrução 
                    if(it.getStage() not in stageInUse): 
                        # pega a próxima instrução no Pipe
                        nextIt = self.__getNextInstrcPipe(it); 
                        #se o seguinte tá carregando e o atual está nos registradores não deve avançar de estagio
                        if(nextIt.getStage() == 0 and it.getStage() == 1 and nextIt.getCicle() == 1): 
                            self.__reportAdvPipe(None, "[  NOOP ] ");
                        else:
                            #Adiciona o estagio para os estagios em uso
                            stageInUse.append(it.getStage());
                            #busca a quantidade de ciclos para a instrução
                            numCiclos = self.__tCicles[it.getType()][self.__pStages[it.getStage()]]; 
                            if(numCiclos == 0):
                                if (it.getStage()!=4):
                                    it.advPipeStage();
                                strAvan = "*";  
                            else:
                                strAvan = " ";
                            if(it.getStage() < len(self.__pStages)):
                                self.__reportAdvPipe(it, strAvan);
                                #se a instrução nao passo o total de ciclos dela (para aquele estagio da instrução)
                                if(it.getCicle() >= numCiclos): 
                                    it.advPipeStage(); #avança o estagio da instrução
                                    it.resetCicle(); #reseta os ciclos da instrução
                                #se nao passo todos os ciclos do estagio avança o ciclo, indicando que deu mais um passo.    
                                else: 
                                    it.advCicle(); #avança o ciclo
                            else:
                                self.__reportAdvPipe(None,"[  NOOP ] ");
                                self.__rmRegs(self.__pMatriz[i].getOperandos());
                                self.__pMatriz[i] = None;
                                self.__pLines -= 1;
                    else:
                        self.__reportAdvPipe(None,"[  NOOP ] ");
                else:
                    self.__reportAdvPipe(None,"[  NOOP ] ");
                    self.__rmRegs(self.__pMatriz[i].getOperandos());
                    self.__pMatriz[i] = None;
                    self.__pLines -= 1;
            else: 
                self.__reportAdvPipe(None,"[       ] ");
            i += 1; 
        print "";
#--------------------------------------------------------------------------------------------------------------
